﻿using SqlSugar;
using System.Linq.Expressions;
using Zhp.SqlSugar.Entity;
using Zhp.SqlSugar.Trigger.Core;
using Zhp.SqlSugar.UnitOfWork;

namespace Zhp.SqlSugar.Repository
{
    public class BaseRepository<TEntity> : SimpleClient<TEntity>, IBaseRepository<TEntity> where TEntity : RootEntityReadOnly<TEntity>, new()
    {
        protected ITriggerExecute TriggerExecute;
        public BaseRepository(IUnitOfWork<SugarUnitOfWork> unitOfWork)
        {
            Context = unitOfWork.Db;
            TriggerExecute = unitOfWork.trigger;
        }


        #region 重写新增，修改，删除，使其带有触发器功能（有外部事务，内部事务用外部事务（推荐）db.Ado.IsNoTran()表示事务为null才开启事务）

        #region 新增

        public override bool Insert(TEntity insertObj)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                ExecuteExtra(uow, insertObj);

                uow.Db.Insertable(insertObj).ExecuteCommand();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.Inserted, insertObj);

                return uow.Commit();
            }
        }

        public override bool InsertRange(TEntity[] insertObjs)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                foreach (var item in insertObjs)
                {
                    ExecuteExtra(uow, item);
                }

                uow.Db.Insertable(insertObjs).ExecuteCommand();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.InsertedRange, insertObjs.ToList());

                return uow.Commit();
            }
        }

        public override bool InsertRange(List<TEntity> insertObjs)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                foreach (var item in insertObjs)
                {
                    ExecuteExtra(uow, item);
                }

                uow.Db.Insertable(insertObjs).ExecuteCommand();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.InsertedRange, insertObjs.ToList());

                return uow.Commit();
            }
        }

        public override async Task<bool> InsertAsync(TEntity insertObj)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                await ExecuteExtraAsync(uow, insertObj);

                await uow.Db.Insertable(insertObj).ExecuteCommandAsync();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.Inserted, insertObj);

                return uow.Commit();
            }
        }

        public override async Task<bool> InsertRangeAsync(TEntity[] insertObjs)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                foreach (var item in insertObjs)
                {
                    await ExecuteExtraAsync(uow, item);
                }

                await uow.Db.Insertable(insertObjs).ExecuteCommandAsync();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.InsertedRange, insertObjs.ToList());

                return uow.Commit();
            }
        }

        public override async Task<bool> InsertRangeAsync(List<TEntity> insertObjs)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                foreach (var item in insertObjs)
                {
                    await ExecuteExtraAsync(uow, item);
                }

                await uow.Db.Insertable(insertObjs).ExecuteCommandAsync();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.InsertedRange, insertObjs);

                return uow.Commit();
            }
        }

        #endregion

        #region 修改

        public override bool Update(TEntity updateObj)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                ExecuteExtra(uow, updateObj);

                uow.Db.Updateable(updateObj).ExecuteCommand();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.Updated, updateObj);

                return uow.Commit();
            }
        }

        public override bool UpdateRange(TEntity[] updateObjs)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                foreach (var item in updateObjs)
                {
                    ExecuteExtra(uow, item);
                }

                uow.Db.Updateable(updateObjs).ExecuteCommand();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.UpdatedRange, updateObjs.ToList());

                return uow.Commit();
            }
        }

        public override bool UpdateRange(List<TEntity> updateObjs)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                foreach (var item in updateObjs)
                {
                    ExecuteExtra(uow, item);
                }

                uow.Db.Updateable(updateObjs).ExecuteCommand();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.UpdatedRange, updateObjs);

                return uow.Commit();
            }
        }

        public override bool Update(Expression<Func<TEntity, TEntity>> columns, Expression<Func<TEntity, bool>> whereExpression)
        {
            var updateObjs = Context.Queryable<TEntity>().Where(whereExpression).ToList();

            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                uow.Db.Updateable<TEntity>().SetColumns(columns).Where(whereExpression).ExecuteCommand();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.UpdatedRange, updateObjs);

                return uow.Commit();
            }
        }

        public override bool UpdateSetColumnsTrue(Expression<Func<TEntity, TEntity>> columns, Expression<Func<TEntity, bool>> whereExpression)
        {
            var updateObjs = Context.Queryable<TEntity>().Where(whereExpression).ToList();

            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                uow.Db.Updateable<TEntity>().SetColumns(columns, appendColumnsByDataFilter: true).Where(whereExpression).ExecuteCommand();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.UpdatedRange, updateObjs);

                return uow.Commit();
            }
        }

        public override async Task<bool> UpdateAsync(TEntity updateObj)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                ExecuteExtra(uow, updateObj);

                await uow.Db.Updateable(updateObj).ExecuteCommandAsync();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.Updated, updateObj);

                return uow.Commit();
            }
        }

        public override async Task<bool> UpdateRangeAsync(TEntity[] updateObjs)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                foreach (var item in updateObjs)
                {
                    await ExecuteExtraAsync(uow, item);
                }
                await uow.Db.Updateable(updateObjs).ExecuteCommandAsync();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.UpdatedRange, updateObjs.ToList());

                return uow.Commit();
            }
        }

        public override async Task<bool> UpdateRangeAsync(List<TEntity> updateObjs)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                foreach (var item in updateObjs)
                {
                    await ExecuteExtraAsync(uow, item);
                }
                await uow.Db.Updateable(updateObjs).ExecuteCommandAsync();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.UpdatedRange, updateObjs);

                return uow.Commit();
            }
        }

        public override async Task<bool> UpdateAsync(Expression<Func<TEntity, TEntity>> columns, Expression<Func<TEntity, bool>> whereExpression)
        {
            var updateObjs = await Context.Queryable<TEntity>().Where(whereExpression).ToListAsync();

            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                await uow.Db.Updateable<TEntity>().SetColumns(columns).Where(whereExpression).ExecuteCommandAsync();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.UpdatedRange, updateObjs);

                return uow.Commit();
            }
        }

        public override async Task<bool> UpdateSetColumnsTrueAsync(Expression<Func<TEntity, TEntity>> columns, Expression<Func<TEntity, bool>> whereExpression)
        {
            var updateObjs = await Context.Queryable<TEntity>().Where(whereExpression).ToListAsync();

            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                await uow.Db.Updateable<TEntity>().SetColumns(columns, appendColumnsByDataFilter: true).Where(whereExpression).ExecuteCommandAsync();

                TriggerExecute.InvokeRange(uow.Db, TriggerEnum.UpdatedRange, updateObjs);

                return uow.Commit();
            }
        }

        public async Task<bool> UpdateColumns(TEntity entity, string[] ignoreColumns, bool isLock = true)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                var up = uow.Db.Updateable(entity);

                up = up.IgnoreColumns(ignoreColumns);

                if (isLock)
                {
                    up = up.With(SqlWith.UpdLock);
                }

                await up.ExecuteCommandAsync();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.Updated, entity);

                return uow.Commit();
            }
        }

        #endregion

        #region 删除

        public override bool Delete(TEntity deleteObj)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                ExecuteExtra(uow, deleteObj);

                uow.Db.Deleteable(deleteObj).IsLogic().ExecuteCommand();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.Deleted, deleteObj);

                return uow.Commit();
            }
        }

        public override bool Delete(List<TEntity> deleteObjs)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                foreach (var item in deleteObjs)
                {
                    ExecuteExtra(uow, item);
                }

                uow.Db.Deleteable(deleteObjs).IsLogic().ExecuteCommand();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.DeletedRange, deleteObjs);

                return uow.Commit();
            }
        }

        public override bool DeleteById(dynamic id)
        {
            var deleteObj = Context.Queryable<TEntity>().InSingle(id);

            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                uow.Db.Deleteable<TEntity>().In(id).IsLogic().ExecuteCommand();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.Deleted, deleteObj);

                return uow.Commit();
            }
        }

        public override bool DeleteByIds(dynamic[] ids)
        {
            var deleteObjs = Context.Queryable<TEntity>().In(ids).ToList();

            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                uow.Db.Deleteable(deleteObjs).IsLogic().ExecuteCommand();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.DeletedRange, deleteObjs);

                return uow.Commit();
            }
        }

        public override async Task<bool> DeleteAsync(TEntity deleteObj)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                await ExecuteExtraAsync(uow, deleteObj);

                await uow.Db.Deleteable(deleteObj).IsLogic().ExecuteCommandAsync();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.Updated, deleteObj);

                return uow.Commit();
            }
        }

        public override async Task<bool> DeleteAsync(List<TEntity> deleteObjs)
        {
            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                foreach (var item in deleteObjs)
                {
                    await ExecuteExtraAsync(uow, item);
                }

                await uow.Db.Deleteable(deleteObjs).IsLogic().ExecuteCommandAsync();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.DeletedRange, deleteObjs);

                return uow.Commit();
            }
        }

        public override async Task<bool> DeleteByIdAsync(dynamic id)
        {
            var deleteObj = await Context.Queryable<TEntity>().InSingleAsync(id);

            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                await uow.Db.Deleteable<TEntity>().In(id).IsLogic().ExecuteCommandAsync();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.Deleted, deleteObj);

                return uow.Commit();
            }
        }

        public override async Task<bool> DeleteByIdsAsync(dynamic[] ids)
        {
            var deleteObjs = await Context.Queryable<TEntity>().In(ids).ToListAsync();

            using (var uow = Context.CreateContext(Context.Ado.IsNoTran()))
            {
                await uow.Db.Deleteable(deleteObjs).IsLogic().ExecuteCommandAsync();

                TriggerExecute.Invoke(uow.Db, TriggerEnum.DeletedRange, deleteObjs);

                return uow.Commit();
            }
        }

        #endregion

        #endregion

        #region Extra

        /// <summary>
        /// 操作额外数据
        /// </summary>
        /// <param name="uow"></param>
        /// <param name="entity"></param>
        private void ExecuteExtra(ISugarUnitOfWork uow, TEntity entity)
        {
            //添加额外数据
            InsertExtra(uow, entity.InsertExtraEntity);

            //修改额外数据
            UpdateExtra(uow, entity.UpdateExtraEntity);

            //删除额外数据
            DeleteExtra(uow, entity.DeleteExtraEntity);
        }

        /// <summary>
        /// 操作额外数据
        /// </summary>
        /// <param name="uow"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        private async Task ExecuteExtraAsync(ISugarUnitOfWork uow, TEntity entity)
        {
            //添加额外数据
            await InsertExtraAsync(uow, entity.InsertExtraEntity);

            //修改额外数据
            await UpdateExtraAsync(uow, entity.UpdateExtraEntity);

            //删除额外数据
            await DeleteExtraAsync(uow, entity.DeleteExtraEntity);
        }

        /// <summary>
        /// 添加额外数据
        /// </summary>
        /// <param name="uow"></param>
        /// <param name="insertObj"></param>
        /// <returns></returns>
        private void InsertExtra(ISugarUnitOfWork uow, List<IRootEntity> insertObj)
        {
            //有可能有嵌套，要循环
            var list = new List<IRootEntity>();

            GetInsertExtraEntity(list, insertObj);

            foreach (var item in list)
            {
                //这里不能直接插入集合，因为类型有可能不同，会抛异常
                uow.Db.InsertableByObject(item).ExecuteCommand();
            }
        }

        /// <summary>
        /// 添加额外数据
        /// </summary>
        /// <param name="uow"></param>
        /// <param name="insertObj"></param>
        /// <returns></returns>
        private async Task InsertExtraAsync(ISugarUnitOfWork uow, List<IRootEntity> insertObj)
        {
            //有可能有嵌套，要循环
            var list = new List<IRootEntity>();

            GetInsertExtraEntity(list, insertObj);

            foreach (var item in list)
            {
                //这里不能直接插入集合，因为类型有可能不同，会抛异常
                await uow.Db.InsertableByObject(item).ExecuteCommandAsync();
            }
        }


        /// <summary>
        /// 更新额外数据
        /// </summary>
        /// <param name="uow"></param>
        /// <param name="updateObj"></param>
        /// <returns></returns>
        private void UpdateExtra(ISugarUnitOfWork uow, List<IRootEntity> updateObj)
        {
            //有可能有嵌套，要循环
            var list = new List<IRootEntity>();

            GetUpdateExtraEntity(list, updateObj);

            foreach (var item in list)
            {
                //这里不能直接插入集合，因为类型有可能不同，会抛异常
                uow.Db.UpdateableByObject(item).ExecuteCommand();
            }
        }

        /// <summary>
        /// 更新额外数据
        /// </summary>
        /// <param name="uow"></param>
        /// <param name="updateObj"></param>
        /// <returns></returns>
        private async Task UpdateExtraAsync(ISugarUnitOfWork uow, List<IRootEntity> updateObj)
        {
            //有可能有嵌套，要循环
            var list = new List<IRootEntity>();

            GetUpdateExtraEntity(list, updateObj);

            foreach (var item in list)
            {
                //这里不能直接插入集合，因为类型有可能不同，会抛异常
                await uow.Db.UpdateableByObject(item).ExecuteCommandAsync();
            }
        }


        /// <summary>
        /// 删除额外数据
        /// </summary>
        /// <param name="uow"></param>
        /// <param name="deleteObj"></param>
        /// <returns></returns>
        private void DeleteExtra(ISugarUnitOfWork uow, List<IRootEntity> deleteObj)
        {
            //有可能有嵌套，要循环
            var list = new List<IRootEntity>();

            GetDeleteExtraEntity(list, deleteObj);

            foreach (var item in list)
            {
                //这里不能直接插入集合，因为类型有可能不同，会抛异常
                item.IsDeleted = true;
                uow.Db.UpdateableByObject(item).ExecuteCommand();
            }
        }

        /// <summary>
        /// 删除额外数据
        /// </summary>
        /// <param name="uow"></param>
        /// <param name="deleteObj"></param>
        /// <returns></returns>
        private async Task DeleteExtraAsync(ISugarUnitOfWork uow, List<IRootEntity> deleteObj)
        {
            //有可能有嵌套，要循环
            var list = new List<IRootEntity>();

            GetDeleteExtraEntity(list, deleteObj);

            foreach (var item in list)
            {
                //这里不能直接插入集合，因为类型有可能不同，会抛异常
                item.IsDeleted = true;
                await uow.Db.UpdateableByObject(item).ExecuteCommandAsync();
            }
        }

        private void GetInsertExtraEntity(List<IRootEntity> list, List<IRootEntity> extraEntityList)
        {
            foreach (var item in extraEntityList)
            {
                list.Add(item);
                if (item.InsertExtraEntity.Any())
                {
                    GetInsertExtraEntity(list, item.InsertExtraEntity);
                }
            }
        }


        private void GetUpdateExtraEntity(List<IRootEntity> list, List<IRootEntity> extraEntityList)
        {
            foreach (var item in extraEntityList)
            {
                list.Add(item);
                if (item.UpdateExtraEntity.Any())
                {
                    GetUpdateExtraEntity(list, item.UpdateExtraEntity);
                }
            }
        }


        private void GetDeleteExtraEntity(List<IRootEntity> list, List<IRootEntity> extraEntityList)
        {
            foreach (var item in extraEntityList)
            {
                list.Add(item);
                if (item.DeleteExtraEntity.Any())
                {
                    GetDeleteExtraEntity(list, item.DeleteExtraEntity);
                }
            }
        }

        #endregion
    }
}
